<?php
/**
 * Vivvo Template Engine
 *
 * LICENSE:    GPL 2.0
 *
 * @package    Vivvo_template_engine
 * @author     Slobodan Utvic <utvara@spoonlabs.com>
 * @copyright  Spoonlabs
 * @license    http://www.vivvo.net/template-engine/license.php
 * @version    0.9
 * @category   template
 */	
	
	/**
	 * Template_node 
	 *
	 * @version Release: 0.9
	 * @license http://www.vivvo.net/template-engine/license.php
	 * @copyright Spoonlabs
	 * @package Vivvo_template_engine
	 * @author Slobodan Utvic <utvara@spoonlabs.com>
	 */

	Class template_node {
		/**
		 * Node type
		 *
		 * @var string values if foreach literal include variable string
		 */
		var $_type;
		
		/**
		 * Template host
		 *
		 * @var template
		 */
		var $_template;
		 
		/**
		 * Template tag value
		 * 
		 * @var string
		 */
		var $_value;
		
		/**
		 * Children nodes
		 * 
		 * @var array Arrya of children template_nodes
		 */
		var $_nodes = array();
		
		/**
		 * Parent node
		 * 
		 * @var mixed template_node or null for root nodes
		 */
		var $parent;
		
		/**
		 * Procces children nodes
		 *		 
		 * @return  string Processing output
		 */
		function nodes_output(){
			$output = '';
			if (is_array($this->_nodes)){				
				foreach ($this->_nodes as $node) {
					if (is_a($node, 'template_node')){
						$output .= $node->get_output();
					}
				}
			}
			return $output;
		}
		
		/**
		 * Process parameters
		 *		 
		 * @param string $param
		 * @return  misc parameter value
		 */
		function param_value($param){
			if (substr($param,0,1) == '$'){
				$varible_name = '';
				preg_match('/\$(\w+)(\[\w+\]|\.\w+)?/', $param, $varible_name);
				$variable = $this->_template->get_value($varible_name[1]);
				if (isset($varible_name[2])){
					if (substr($varible_name[2],0,1) == '['){
						if (is_array($variable)){
							$key = trim($varible_name[2],'[]');
							if (array_key_exists($key, $variable)){
								$variable = $variable[$key];
							}else {
								$variable = '';
							}
						}else {
							$variable = '';
						}
					}elseif (substr($varible_name[2],0,1) == '.'){							
						if (is_object($variable)){								
							$property = substr($varible_name[2],1);
							if (@isset($variable->$property)){									
								$variable = $variable->$property;
							}elseif (method_exists($variable, $property)){
								$variable = call_user_func(array($variable, $property));
							}else {
								$variable = '';
							}
						}else {
							$variable = '';
						}
					}
				}
				$value = $variable;
			}else{
				if (substr($param,0,1) == '"'){
					$value = trim($param, '"');
				}elseif	(substr($param,0,1) == "'"){
					$value = trim($param, "'");
				}else {
					$value = $param;
				}
			}
			return $value;
		}
		
		/**
		 * Generate node output
		 * 
		 * @return string Parsed value
		 */	
		function get_output(){
			$output = '';
			switch ($this->_type){		
				
				// Parse if node		
				case 'if':					
					$countinue = false;
					preg_match('/\{if\s+(\'[^\']*\'|\"[^"]*\"|\d+|true|false|\$[\w\.\[\]]+)\s*(eq|neq|lt|gt)\s*(\'[^\']*\'|\"[^"]*\"|\d+|true|false|\$[\w\.\[\]]+)\s*\}/i',$this->_value, $if_statment);

					$variable = $this->param_value($if_statment[1]);
					$value = $this->param_value($if_statment[3]);
					
					switch ($if_statment[2]){
						case 'eq':
							if ($variable == $value) $countinue = true;
							break;
						case 'neq':
							if ($variable != $value) $countinue = true;

							break;
						case 'lt':
							if ($variable < $value) $countinue = true;
							break;
						case 'gt':
							if ($variable > $value) $countinue = true;
							break;
					}
					
					if ($countinue){
						$output .= $this->nodes_output();
					}					
					return $output;
				
				// Parse foreach node
				case 'foreach':					
					preg_match('/\{foreach\s+item\s*=\s*\$(\w+)\s+from\s*=\s*(\$[\w\.\[\]]+)\s*\}/',$this->_value, $for_statment);
					$array = $this->param_value($for_statment[2]);
					if (is_array($array)){
						foreach ($array as $item){
							$this->_template->assign($for_statment[1], $item);
							$output .= $this->nodes_output();
						}
					}
					return $output;
					
				// Parse literaln node
				case 'literal':
					return $this->_value;
				
				// Parse include node
				case 'include':
					$output = '';
					preg_match('/\{include\s+(file\s*=\s*\'[^\']*\'|url\s*=\s*\'[^"]*\')\s?(parse)?(.*)\}/',$this->_value, $include_statment);
					list($type, $href) = explode('=',$include_statment[1]);
					$href = trim($href, " \t'");
					// include file
					if (trim($type) == 'file'){
						if (is_file(TEMPLATE_ROOT . $href)){							
							$output = file_get_contents(TEMPLATE_ROOT . $href);
						}
						if (isset($include_statment[2]) && $include_statment[2] === 'parse'){
							$tmp_tpl = new template();
							$tmp_tpl->set_string_template($output);
							
							preg_match_all('/(\w+)\s*=\s*(\'[^\']*\'|\"[^"]*\"|\d+|true|false|\$[\w\.\[\]]+)?\s*/',trim($include_statment[3]), $variable_statment);
							if (is_array($variable_statment[1])){
								foreach ($variable_statment[1] as $k => $v){
									$tmp_tpl->assign($v, $this->param_value($variable_statment[2][$k]));
								}
							}
							$output = $tmp_tpl->get_output();
						}
					}
					// include url
					if (trim($type) == 'url')	{
						$output = file_get_contents($href);
					}					
					return $output;
				
				// Parse variable node
				case 'variable':				
					return $this->param_value(trim($this->_value,'{}'));
				
				// Parse string node
				case 'string':					
					return $this->_value;
					
				// Parse children nodes
				default:
					return $output .= $this->nodes_output();
			}
		}

		/**
		 * Constructor
		 * 
		 * @param  string      $type       Node type
		 * @param  template    $template   Template host
		 * @param  string      $value      Template tag value
		 * @return template_node
		 */	
		function template_node($type, &$template, $value = ''){
			$this->_template =& $template;
			$this->_type = $type;
			$this->_value = $value;
		}
	}
	
	/**
	 * Template
	 *
	 * @version Release: 0.9
	 * @license http://www.vivvo.net/template-engine/license.php
	 * @copyright Spoonlabs
	 * @package Vivvo_template_engine
	 * @author Slobodan Utvic <utvara@spoonlabs.com>
	 */
	
	Class template{		
		/**
		 * Output
		 *
		 * @var string
		 */
		var $output;
		
		/**
		 * Patterns
		 *
		 * @var array
		 */
		var $_replacments = array();
		
		/**
		 * Nessted templates
		 *
		 * @var array
		 */
		var $_nessted = array();		
		
		/**
		 * Template file name
		 *
		 * @var string
		 */
		var $_template_file = '';
		
		/**
		 * String template for parsing, 
		 * if template file is set and string is not empty, 
		 * this template will be used
		 *
		 * @var string
		 */
		var $_string_template = '';

		/**
		 * Push sub_template, pattern assignment
		 *		 
		 * @param strng $pattern
		 * @param template $value
		 */		
		function assign_template($pattern, &$value){
			if ($pattern != ''){
				if (is_object($value) && is_a($value, 'template')){
					$this->_nessted[$pattern] =& $value;	
				}
			}
		}
				
		/**
		 * Push value, pattern assignment
		 *		 
		 * @param string $pattern
		 * @param misc $value
		 */		
		function assign($pattern, $value){
			if ($pattern != ''){
				$this->_replacments[$pattern] = $value;
			}
		}

		/**
		 * Get value
		 *		 
		 * @param   string  $pattern 
		 * @return  misc    value, object, constant or template output assigned to pattern
		 */				
		function get_value($pattern){
			$value = '';
			if (is_array($this->_replacments) && key_exists($pattern, $this->_replacments)){
				$value = $this->_replacments[$pattern];
			}elseif (is_array($this->_nessted) && key_exists($pattern, $this->_nessted)){
				if (is_object($this->_nessted[$pattern]) && is_a($this->_nessted[$pattern], 'template')){
					$value = $this->_nessted[$pattern]->get_output();
				}
			}elseif (defined($pattern)){
				return constant($pattern);				
			}
			return $value;
		}
			
		/**
		 * Template file name
		 *
		 * @param string $file
		 */
		function set_template_file($file){
			$this->_template_file = $file;
		}
		
		/**
		 * Get template file content
		 *
		 */
		function load_template(){			
			$this->_string_template = file_get_contents($this->_template_file);
		}
				
		/**
		 * Parse template file
		 *
		 */
		function parse(){
			$split_tpl = preg_split('/({.*?})/', $this->_string_template,-1,PREG_SPLIT_DELIM_CAPTURE);
			$nodes = array();
			$parent_stack = array(-1);
			$literal = false;
			if (is_array($split_tpl)){
				$i = 0;
				for($j = 0; $j< count($split_tpl); $j++){
					$chunk = $split_tpl[$j];
					if ($chunk != ''){
						//close literal node
						if ($literal && (substr($chunk, 0, 10) != '{/literal}')){
							$nodes[$i] = new template_node('string', $this, $chunk);
							if ($parent_stack[count($parent_stack) - 1] != -1){
								$nodes[$parent_stack[count($parent_stack) - 1]]->_nodes[] =& $nodes[$i];
							}
							$nodes[$i]->parent = $parent_stack[count($parent_stack) - 1];
							$i++;
						
						//if node
						}elseif (substr($chunk, 0, 4) == '{if '){														
							$nodes[$i] = new template_node('if', $this, $chunk);
							if ($parent_stack[count($parent_stack) - 1] != -1){
								$nodes[$parent_stack[count($parent_stack) - 1]]->_nodes[] =& $nodes[$i];
							}
							$nodes[$i]->parent = $parent_stack[count($parent_stack) - 1];
							array_push($parent_stack, $i);
							$i++;
						
						//close if node
						}elseif (substr($chunk, 0, 5) == '{/if}'){
							array_pop($parent_stack);
							
						//foreache node
						}elseif (substr($chunk, 0, 9) == '{foreach '){
							$nodes[$i] = new template_node('foreach', $this, $chunk);
							if ($parent_stack[count($parent_stack) - 1] != -1){
								$nodes[$parent_stack[count($parent_stack) - 1]]->_nodes[] =& $nodes[$i];
							}
							$nodes[$i]->parent = $parent_stack[count($parent_stack) - 1];
							array_push($parent_stack, $i);
							$i++;
							
						//close foreache node
						}elseif (substr($chunk, 0, 10) == '{/foreach}'){
							array_pop($parent_stack);
						
						//literal node
						}elseif ((substr($chunk, 0, 9) == '{literal}')){
							$literal = true;
							
						//close literal node
						}elseif ((substr($chunk, 0, 10) == '{/literal}')){						
							$literal = false;
												
						//variable node
						}elseif (preg_match('/\{\$[\w|\.|\[|\]]+\}/', $chunk)){
							$nodes[$i] = new template_node('variable', $this, $chunk);
							if ($parent_stack[count($parent_stack) - 1] != -1){
								$nodes[$parent_stack[count($parent_stack) - 1]]->_nodes[] =& $nodes[$i];
							}
							$nodes[$i]->parent = $parent_stack[count($parent_stack) - 1];
							$i++;
														
						// include node
						}elseif (substr($chunk, 0, 9) == '{include '){
							$nodes[$i] = new template_node('include', $this, $chunk);							
							if ($parent_stack[count($parent_stack) - 1] != -1){
								$nodes[$parent_stack[count($parent_stack) - 1]]->_nodes[] =& $nodes[$i];
							}
							$nodes[$i]->parent = $parent_stack[count($parent_stack) - 1];
							$i++;
							
						//string node
						}else{
							$nodes[$i] = new template_node('string', $this, $chunk);
							if ($parent_stack[count($parent_stack) - 1] != -1){
								$nodes[$parent_stack[count($parent_stack) - 1]]->_nodes[] =& $nodes[$i];
							}
							$nodes[$i]->parent = $parent_stack[count($parent_stack) - 1];
							$i++;
						}
					}
				}
			}			
			
			foreach ($nodes as $node){
				if ($node->parent == -1){ 
					$this->output .= $node->get_output();
				}
			}
		}
		
		/**
		 * Set string template
		 *
		 * @param string $str_template
		 */
		function set_string_template($str_template){			
			$this->_string_template = $str_template;
		}
		
		/**
		 * Get output
		 *
		 * @return string
		 */
		function get_output(){
			if ($this->_string_template == ''){
				$this->load_template();					
			}
			$this->parse();			
			return $this->output;
		}
		
		/**
		 * Constructor
		 * 
		 * @return template
		 */	
		function template(){

		}
	}
?>